贪心算法_哈夫曼编码问题(Huffman Coding)

       哈夫曼编码问题,可以用贪心算法来解还是比较爽的啊(上一篇博客我还在想贪心算法能解的问题不多)。下面来详细讨论一下哈夫曼编码问题。

      问题描述:现有一个文本文件,其中包含的字符数据出现的次数各不相同,先要求对该文本中包含的字符进行编码,使文本占用的位数更小。

      问题分析:我们知道文件的存储都是以二进制数表示的,如:字符c可以表示为010101...之类的。因为不同的操作系统对于不同的数据类型会分配给相同的数据容器长度,如java中int型数据固定占用4个字节的存储空间。现在问题时因为各个字符出现的概率不同,那么我们就可以给出现概率高的字符分配以"短"的二进制表示数,给出现概率低的字符分配以"长"的二进制表示数。从而达到降低平均每字符占用的空间数,进而实现无损的空间压缩。

      OK,我们来论证哈夫曼编码问题的贪心选择性质。这里必须介绍一下的是,我们会使用二叉树这种数据结构来解哈夫曼问题。从根节点到叶节点经过的路径就是某个叶节点对象(这里就是字符)的编码值。那么从直觉(恩,直觉,我觉的解贪心算法的话,直觉很重要)上将讲,应该将概率低的元素放置到树的底部,将概率高的元素放置到树的顶部。代码如下:

package com.wly.algorithmbase.greedy;

import java.util.ArrayList;

/**
 * 贪心算法解哈夫曼编码问题
 * 
 * @author wly
 * 
 */
public class HuffmanCode {

	public static void main(String[] args) {

		ArrayList<HuffmanNode> list = new ArrayList<HuffmanNode>();

		list.add(new HuffmanNode(null, null, "A", 0.3f));
		list.add(new HuffmanNode(null, null, "B", 0.1f));
		list.add(new HuffmanNode(null, null, "C", 0.35f));
		list.add(new HuffmanNode(null, null, "D", 0.05f));
		list.add(new HuffmanNode(null, null, "E", 0.2f));

		print(getHuffmanCodeNode(list));
	}

	/**
	 * 得到表示当前输入节点的树结构
	 * @param list
	 * @return
	 */
	public static HuffmanNode getHuffmanCodeNode(ArrayList<HuffmanNode> list) {

		while (list.size() >= 2) {
			//1.排序元素
			srotNodeListByKey(list);

			//2.合并key值最小的两个节点(因为已经排序过了,此处就是列表的前两项)
			HuffmanNode newNode = combine2SmallestNode(list.get(0), list.get(1));
			list.remove(0);
			list.remove(0); //注意ArrayList中remove元素时的索引移动s
			list.add(0, newNode);
		}
		
		return list.get(0);
	}
	
	/**
	 * 打印某个节点的树结构,即以该节点为根节点的子树结构s
	 * @param node
	 */
	public static void print(HuffmanNode node) {
		System.out.print("| " + node.getData() + "," + node.getPercent() + " |");
		if(node.getLeftN() != null) {
			print(node.getLeftN());
		} 
		
		if(node.getRightN() != null) {
			print(node.getRightN());
		} 
	}	
	
	/**
	 * 使用冒泡排序,按key值单调递增排序
	 * 
	 * @param list
	 */
	public static void srotNodeListByKey(ArrayList<HuffmanNode> list) {
		for (int i = 0; i < list.size(); i++) {
			for (int j = i+1; j < list.size(); j++) {
				if (list.get(i).getPercent() > list.get(j).getPercent()) {
					// 交换位置
					list.add(i, list.get(j));
					list.remove(j+1);
					
					list.add(j, list.get(i + 1));
					list.remove(i + 1);
				}
			}
		}
	}


	/**
	 * 将两个子节点合成为一个父节点
	 * 
	 * @param leftNode
	 * @param rightNode
	 * @return
	 */
	private static HuffmanNode combine2SmallestNode(HuffmanNode leftNode,
			HuffmanNode rightNode) {
		HuffmanNode parentNode = new HuffmanNode(leftNode, rightNode,
				leftNode.getData() + rightNode.getData(), leftNode.getPercent()
						+ rightNode.getPercent());
		return parentNode;
	}
}

/**
 * 用于表示哈夫曼编码的二叉树的节类
 * 
 * @author wly
 * 
 */
class HuffmanNode {

	private HuffmanNode leftN; //左子节点
	private HuffmanNode rightN; //右子节点
	private String data; // 包含的数据,本程序中指的是字符
	private float percent; // 检索key值

	public HuffmanNode(HuffmanNode leftN, HuffmanNode rightN, String data,
			float key) {
		super();
		this.leftN = leftN;
		this.rightN = rightN;
		this.data = data;
		this.percent = key;
	}

	public float getPercent() {
		return percent;
	}

	public void setPercent(float percent) {
		this.percent = percent;
	}

	public HuffmanNode getLeftN() {
		return leftN;
	}

	public void setLeftN(HuffmanNode leftN) {
		this.leftN = leftN;
	}

	public HuffmanNode getRightN() {
		return rightN;
	}

	public void setRightN(HuffmanNode rightN) {
		this.rightN = rightN;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

}
      运行结果:
| DBEAC,1.0 || DBE,0.35000002 || DB,0.15 || D,0.05 || B,0.1 || E,0.2 || AC,0.65 || A,0.3 || C,0.35 |

      从运行结果可以得到二叉树如下:

      即各个字符的编码分别是:D:000、D:001、E:01、A:10、C:11

      若不进行编码按相同字长编码,则至少需要3位,那么需要存储ABCDE的尺寸为:3*1=3

      编码后,存储ABCDE的尺寸为:3*0.05+3*0.1+2*0.2+2*0.3+2*0.2=1.85

      可见使用哈夫曼编码能够在一定程度上实现数据的无损压缩。

      O啦~~~

      转载请保留出处:http://blog.csdn.net/u011638883/article/details/16857309

      谢谢!!

 

  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
哈夫曼编码是一种压缩算法,可以将一串数据压缩为更短的编码序列,从而节省存储空间。贪心算法可以用来实现哈夫曼编码,具体步骤如下: 1. 统计每个字符出现的频率,并将每个字符看作一个节点。 2. 将这些节点入一个优先队列中,按照节点的频率从小到大排序。 3. 从队列中取出频率最小的个节点,将它们合并成一个新节点,新节点的频率等于这个节点的频率之和。将新节点插入队列中。 4. 重复步骤3,直到队列中只剩一个节点,这个节点就是哈夫曼树的根节点。 5. 对哈夫曼树进行遍历,将每个字符的编码记录下来。遍历,从根节点出发,如果走左子树就记录一个0,如果走右子树就记录一个1。当到达叶子节点,记录下这个字符的编码。 代码实现如下: ```python import heapq # 定义哈夫曼树节点类 class Node: def __init__(self, freq, char=None): self.freq = freq self.char = char self.left = None self.right = None def __lt__(self, other): return self.freq < other.freq # 统计字符频率 def count_freq(text): freq = {} for char in text: if char in freq: freq[char] += 1 else: freq[char] = 1 return freq # 构建哈夫曼树 def build_huffman_tree(freq): heap = [Node(freq[char], char) for char in freq] heapq.heapify(heap) while len(heap) > 1: node1 = heapq.heappop(heap) node2 = heapq.heappop(heap) merged = Node(node1.freq + node2.freq) merged.left = node1 merged.right = node2 heapq.heappush(heap, merged) return heap[0] # 遍历哈夫曼树并记录编码 def traverse_tree(node, code, codes): if node.char: codes[node.char] = code else: traverse_tree(node.left, code + '0', codes) traverse_tree(node.right, code + '1', codes) # 哈夫曼编码 def huffman_encoding(text): freq = count_freq(text) root = build_huffman_tree(freq) codes = {} traverse_tree(root, '', codes) encoded_text = ''.join(codes[char] for char in text) return encoded_text, codes # 哈夫曼解码 def huffman_decoding(encoded_text, codes): inv_codes = {code: char for char, code in codes.items()} decoded_text = '' code = '' for bit in encoded_text: code += bit if code in inv_codes: decoded_text += inv_codes[code] code = '' return decoded_text # 测试 text = 'hello world' encoded_text, codes = huffman_encoding(text) print('Encoded text:', encoded_text) print('Huffman codes:', codes) decoded_text = huffman_decoding(encoded_text, codes) print('Decoded text:', decoded_text) ``` 在以上代码中,我们首先定义了一个 `Node` 类来表示哈夫曼树的节点。然后,我们实现了 `count_freq` 函数来统计字符频率,`build_huffman_tree` 函数来构建哈夫曼树,`traverse_tree` 函数来遍历哈夫曼树并记录编码,以及 `huffman_encoding` 和 `huffman_decoding` 函数来实现哈夫曼编码和解码的功能。最后,我们对代码进行了测试,输出了编码后的文本、哈夫曼编码表和解码后的文本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值